1 package edu.jiangxin.apktoolbox.file.password.recovery.category.dictionary.multithread;
2
3 import edu.jiangxin.apktoolbox.file.core.EncoderDetector;
4 import edu.jiangxin.apktoolbox.file.password.recovery.RecoveryPanel;
5 import edu.jiangxin.apktoolbox.file.password.recovery.State;
6 import org.apache.logging.log4j.LogManager;
7 import org.apache.logging.log4j.Logger;
8
9 import java.io.*;
10 import java.nio.MappedByteBuffer;
11 import java.nio.channels.FileChannel;
12 import java.util.HashSet;
13 import java.util.Set;
14 import java.util.concurrent.BrokenBarrierException;
15 import java.util.concurrent.CyclicBarrier;
16 import java.util.concurrent.ScheduledThreadPoolExecutor;
17 import java.util.concurrent.atomic.AtomicBoolean;
18 import java.util.concurrent.atomic.AtomicInteger;
19
20 public class BigFileReader {
21 private static final Logger logger = LogManager.getLogger(BigFileReader.class.getSimpleName());
22
23 private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024;
24
25 private static final int PROCESSOR_COUNT = Runtime.getRuntime().availableProcessors();
26
27 private final String charset;
28 private final int bufferSize;
29 private final ScheduledThreadPoolExecutor executorService;
30 private final long fileLength;
31 private RandomAccessFile rAccessFile;
32 private final Set<StartEndPair> startEndPairs;
33 private CyclicBarrier cyclicBarrier;
34 private final AtomicInteger counter = new AtomicInteger(0);
35 private final CompleteCallback callback;
36
37 private final AtomicBoolean success = new AtomicBoolean(false);
38
39 private final RecoveryPanel panel;
40
41 public BigFileReader(CompleteCallback callback, RecoveryPanel panel) {
42 this.panel = panel;
43 File file = panel.getDictionaryFile();
44 if (!file.exists()) {
45 throw new IllegalArgumentException("文件不存在!");
46 }
47 this.fileLength = file.length();
48 this.charset = EncoderDetector.judgeFile(file.getAbsolutePath());
49 this.bufferSize = DEFAULT_BUFFER_SIZE;
50 try {
51 this.rAccessFile = new RandomAccessFile(file, "r");
52 } catch (FileNotFoundException e) {
53 logger.error("BigFileReader FileNotFoundException");
54 }
55 this.executorService = new ScheduledThreadPoolExecutor(PROCESSOR_COUNT);
56 this.startEndPairs = new HashSet<>();
57 this.callback = callback;
58 }
59
60 public void start() {
61 long everySize = fileLength / PROCESSOR_COUNT;
62 try {
63 calculateStartEnd(0, everySize);
64 } catch (IOException e) {
65 logger.error("start", e);
66 return;
67 }
68
69 final long startTime = System.currentTimeMillis();
70 int parties = startEndPairs.size();
71 logger.info("[TaskTracing]Parties: " + parties);
72 cyclicBarrier = new CyclicBarrier(parties, () -> {
73 logger.info("use time: " + (System.currentTimeMillis() - startTime) + "ms");
74 logger.info("all line: " + counter.get());
75 callback.onComplete(null);
76 });
77 for (StartEndPair pair : startEndPairs) {
78 logger.info("pair: " + pair);
79 executorService.execute(new SliceReaderTask(pair));
80 }
81 }
82
83 private void calculateStartEnd(long start, long size) throws IOException {
84 if (start > fileLength - 1) {
85 return;
86 }
87 StartEndPair pair = new StartEndPair();
88 pair.start = start;
89 long endPosition = start + size - 1;
90 if (endPosition >= fileLength - 1) {
91 pair.end = fileLength - 1;
92 startEndPairs.add(pair);
93 return;
94 }
95
96 rAccessFile.seek(endPosition);
97 byte tmp = (byte) rAccessFile.read();
98 while (tmp != '\n' && tmp != '\r') {
99 endPosition++;
100 if (endPosition >= fileLength - 1) {
101 endPosition = fileLength - 1;
102 break;
103 }
104 rAccessFile.seek(endPosition);
105 tmp = (byte) rAccessFile.read();
106 }
107 pair.end = endPosition;
108 startEndPairs.add(pair);
109
110 calculateStartEnd(endPosition + 1, size);
111 }
112
113 public void shutdown() {
114 try {
115 rAccessFile.close();
116 } catch (IOException e) {
117 logger.error("shutdown IOException");
118 }
119 executorService.shutdown();
120 logger.info("shutdown executorService");
121 }
122
123 private void handle(byte[] bytes) throws UnsupportedEncodingException {
124 if (success.compareAndSet(true, true) || panel.getCurrentState() != State.WORKING) {
125 return;
126 }
127
128 String line;
129 if (charset == null) {
130 line = new String(bytes);
131 } else {
132 line = new String(bytes, charset);
133 }
134
135 panel.setCurrentPassword(line);
136 panel.increaseProgressBarValue();
137 counter.decrementAndGet();
138
139 if (panel.getCurrentFileChecker().checkPassword(line)) {
140 if (success.compareAndSet(false, true)) {
141 logger.info("find password: {}", line);
142 callback.onComplete(line);
143 }
144 } else {
145 if (!success.compareAndSet(true, true) && panel.getCurrentState() == State.WORKING) {
146 logger.info("try password[{}] failed", line);
147 }
148 }
149 }
150
151 private static class StartEndPair {
152 public long start;
153 public long end;
154
155 @Override
156 public String toString() {
157 return "star=" + start + ";end=" + end;
158 }
159
160 @Override
161 public int hashCode() {
162 final int prime = 31;
163 int result = 1;
164 result = prime * result + (int) (end ^ (end >>> 32));
165 result = prime * result + (int) (start ^ (start >>> 32));
166 return result;
167 }
168
169 @Override
170 public boolean equals(Object obj) {
171 if (this == obj)
172 return true;
173 if (obj == null)
174 return false;
175 if (getClass() != obj.getClass())
176 return false;
177 StartEndPair other = (StartEndPair) obj;
178 if (end != other.end)
179 return false;
180 return start == other.start;
181 }
182
183 }
184
185 private class SliceReaderTask implements Runnable {
186 private final long start;
187 private final long sliceSize;
188 private final byte[] readBuff;
189
190 public SliceReaderTask(StartEndPair pair) {
191 this.start = pair.start;
192 this.sliceSize = pair.end - pair.start + 1;
193 this.readBuff = new byte[bufferSize];
194 }
195
196 @Override
197 public void run() {
198 try {
199 MappedByteBuffer mapBuffer = rAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, start, this.sliceSize);
200 ByteArrayOutputStream bos = new ByteArrayOutputStream();
201 for (int offset = 0; offset < sliceSize; offset += bufferSize) {
202 int readLength;
203 if (offset + bufferSize <= sliceSize) {
204 readLength = bufferSize;
205 } else {
206 readLength = (int) (sliceSize - offset);
207 }
208 mapBuffer.get(readBuff, 0, readLength);
209 for (int i = 0; i < readLength; i++) {
210 byte tmp = readBuff[i];
211 if (tmp == '\n' || tmp == '\r') {
212 if (bos.size() > 0) {
213 handle(bos.toByteArray());
214 }
215 bos.reset();
216 } else {
217 bos.write(tmp);
218 }
219 }
220 }
221 if (bos.size() > 0) {
222 handle(bos.toByteArray());
223 }
224 logger.info("[TaskTracing]Waiting number: " + cyclicBarrier.getNumberWaiting());
225 } catch (Exception e) {
226 logger.error("run Exception" + e.getMessage());
227 }
228 try {
229 cyclicBarrier.await();
230 } catch (InterruptedException e) {
231 logger.error("await InterruptedException");
232 Thread.currentThread().interrupt();
233 } catch (BrokenBarrierException e) {
234 logger.error("await BrokenBarrierException");
235 }
236 }
237
238 }
239 }